package io.vividcode.happyride.addressservice.grpc;

import io.grpc.stub.StreamObserver;
import io.vividcode.happyride.addressservice.api.AddressVO;
import io.vividcode.happyride.addressservice.api.AreaVO;
import io.vividcode.happyride.addressservice.grpc.AddressServiceGrpc.AddressServiceImplBase;
import io.vividcode.happyride.addressservice.service.AddressService;
import io.vividcode.happyride.addressservice.service.AreaService;

import java.util.stream.Collectors;

import net.devh.boot.grpc.server.service.GrpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

@GrpcService
public class AddressGrpcService extends AddressServiceImplBase {

    @Autowired
    AddressService addressService;

    @Autowired
    AreaService areaService;

    private static final Logger LOGGER = LoggerFactory
            .getLogger(AddressGrpcService.class);

    @Override
    public void getAddress(GetAddressRequest request,
                           StreamObserver<GetAddressResponse> responseObserver) {
        GetAddressResponse.Builder builder = GetAddressResponse.newBuilder();
        this.addressService
                .getAddress(request.getAddressId(), request.getAreaLevel())
                .ifPresent(address -> builder.setAddress(this.buildAddress(address)));
        responseObserver.onNext(builder.build());
        responseObserver.onCompleted();
    }

    @Override
    public void getArea(GetAreaRequest request,
                        StreamObserver<GetAreaResponse> responseObserver) {
        GetAreaResponse.Builder builder = GetAreaResponse.newBuilder();
        this.areaService.getArea(request.getAreaCode(), request.getAncestorLevel())
                .ifPresent(area -> builder.setArea(this.buildArea(area)));
        responseObserver.onNext(builder.build());
        responseObserver.onCompleted();
    }

    @Override
    public void search(AddressSearchRequest request,
                       StreamObserver<Address> responseObserver) {
        this.addressService.search(request.getAreaCode(), request.getQuery())
                .forEach(
                        address -> responseObserver.onNext(this.buildAddress(address)));
        responseObserver.onCompleted();
    }

    @Override
    public StreamObserver<GetAddressRequest> getAddresses(
            StreamObserver<Address> responseObserver) {
        return new StreamObserver<>() {
            @Override
            public void onNext(GetAddressRequest request) {
                AddressGrpcService.this.addressService
                        .getAddress(request.getAddressId(), request.getAreaLevel())
                        .ifPresent(
                                address -> responseObserver.onNext(
                                        AddressGrpcService.this.buildAddress(address)));
            }

            @Override
            public void onError(Throwable t) {
                AddressGrpcService.LOGGER.warn("Error", t);
            }

            @Override
            public void onCompleted() {
                responseObserver.onCompleted();
            }
        };
    }

    private Address buildAddress(AddressVO address) {
        return Address.newBuilder().setId(address.getId())
                .setAreaId(address.getAreaId())
                .setAddressLine(address.getAddressLine())
                .setLat(address.getLat().toPlainString())
                .setLng(address.getLng().toPlainString())
                .addAllAreas(address.getAreas().stream()
                        .map(this::buildArea).collect(Collectors.toList()))
                .build();
    }

    private Area buildArea(AreaVO area) {
        return Area.newBuilder().setId(area.getId())
                .setLevel(area.getLevel())
                .setParentCode(area.getParentCode())
                .setAreaCode(area.getAreaCode())
                .setZipCode(area.getZipCode())
                .setCityCode(area.getCityCode())
                .setName(area.getName())
                .setShortName(area.getShortName())
                .setMergerName(area.getMergerName())
                .setPinyin(area.getPinyin())
                .setLat(area.getLat().toPlainString())
                .setLng(area.getLng().toPlainString())
                .build();
    }
}
